#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <Judy.h>

#include "normalization_globals.h"
#include "normalized_datatypes.h"

Pvoid_t normalized_conjunctions;

static unsigned char buffer[16];
#define BUILD_EXISTS_RESTRICTION_ID(r,f) snprintf((char*) buffer,16,"%d_%d",r,f)

// get the existential restriction with role r and filler f
NormalizedConcept* get_exists_restriction(int r, int f) {
	PWord_t pvalue;

	BUILD_EXISTS_RESTRICTION_ID(r,f);
	JSLG(pvalue, normalized_exists_restrictions, buffer); 
	if (pvalue == NULL)
		return NULL;
	return (NormalizedConcept*) *pvalue;
}

void put_exists_restriction(int r, int f, NormalizedConcept* c) {
	PWord_t pvalue;

	BUILD_EXISTS_RESTRICTION_ID(r,f);
	JSLI(pvalue, normalized_exists_restrictions, buffer);
	if (pvalue == PJERR) {
		fprintf(stderr, "could not insert existential restriction (some %d %d), aborting", r, f);
		exit(-1);
	}
	*pvalue = (Word_t) c;
}

NormalizedConcept* create_normalized_exists(NormalizedRole* r, NormalizedConcept* f) {
	NormalizedConcept* c;
	NormalizedExists** tmp;

	// first check if we already created a normalized existential 
	// restriction with the same role and filler
	if ((c = get_exists_restriction(r->description.atomic->id,f->description.atomic->id)) != NULL) {
		return c;
	}
	// if it does not already exist, create it
	c = (NormalizedConcept*) malloc(sizeof(NormalizedConcept));
	assert(c != NULL);
 	c->type = NORMALIZED_EXISTENTIAL_RESTRICTION;
 	c->description.exists = (NormalizedExists*) malloc(sizeof(NormalizedExists));
 	assert(c->description.exists != NULL);
 	c->description.exists->role = r;
 	c->description.exists->filler = f;

	c->description.exists->subsumees = (Pvoid_t) NULL;

	// c->singleton_premises_count = 0;
	// c->singleton_premises = NULL;
	c->containing_premises_count = 0;
	c->containing_premises = NULL;

	++generated_exists_restriction_count;

	put_exists_restriction(r->description.atomic->id,f->description.atomic->id,c);
	// add filler of information to f
	tmp = realloc(f->description.atomic->filler_of, (f->description.atomic->number_of_filler_of + 1) * sizeof(NormalizedExists*));
	assert(tmp != NULL);
	f->description.atomic->filler_of = tmp;
	f->description.atomic->filler_of[f->description.atomic->number_of_filler_of] = c->description.exists;
	++(f->description.atomic->number_of_filler_of);
	
 	return c;
}

/*
void put_generated_atomic_concept_side(NormalizedConcept* generated_atomic_concept, enum axiom_side side) {
	int judy_return;

	switch (side) {
		case LEFT_HANDSIDE:
			J1S(judy_return, generated_atomic_concepts_left, (Word_t) generated_atomic_concept);
			if (judy_return == JERR) {
				fprintf(stderr, "could not set generated atomic concept on the left handside, aborting!\n");
				exit(-1);
			}
			break;
		case RIGHT_HANDSIDE:
			J1S(judy_return, generated_atomic_concepts_right, (Word_t) generated_atomic_concept);
			if (judy_return == JERR) {
				fprintf(stderr, "could not set generated atomic concept on the right handside, aborting!\n");
				exit(-1);
			}
			break;	
		default:
			fprintf(stderr, "axiom side undefined, aborting\n");
			exit(-1);
	}
}
*/

void put_generated_atomic_concept(NormalizedConcept* generated_atomic_concept, NormalizedConcept* normalized_filler, enum axiom_side side) {
	Word_t* pvalue;

	switch (side) {
		case LEFT_HANDSIDE:
			JLI(pvalue, generated_atomic_concepts_left, (Word_t) normalized_filler);
			if (pvalue == PJERR) {
				fprintf(stderr, "could not insert generated atomic concept, aborting!\n");
				exit(-1);
			}
			break;
		case RIGHT_HANDSIDE:
			JLI(pvalue, generated_atomic_concepts_right, (Word_t) normalized_filler);
			if (pvalue == PJERR) {
				fprintf(stderr, "could not insert generated atomic concept, aborting!\n");
				exit(-1);
			}
			break;	
		default:
			fprintf(stderr, "axiom side undefined, aborting\n");
			exit(-1);
	}
	*pvalue = (Word_t) generated_atomic_concept;
}

NormalizedConcept* get_generated_atomic_concept(NormalizedConcept* normalized_filler, enum axiom_side side) {
	PWord_t pvalue;

	switch (side) {
		case LEFT_HANDSIDE:
			JLG(pvalue, generated_atomic_concepts_left, (Word_t) normalized_filler); 
			break;
		case RIGHT_HANDSIDE:
			JLG(pvalue, generated_atomic_concepts_right, (Word_t) normalized_filler); 
			break;
	}
	if (pvalue == NULL)
		return NULL;
	return (NormalizedConcept*) *pvalue;
}

/*
int generated_atomic_concept_for_side(NormalizedConcept* generated_atomic_concept, enum axiom_side side) {
	int side_generated;
	switch (side) {
		case LEFT_HANDSIDE:
			J1T(side_generated, generated_atomic_concepts_left, (Word_t) generated_atomic_concept);
			break;
		case RIGHT_HANDSIDE:
			J1T(side_generated, generated_atomic_concepts_right, (Word_t) generated_atomic_concept);
			break;
		default:
			fprintf(stderr, "axiom side undefined, aborting\n");
			exit(-1);
	}
	return side_generated;
}
*/

void add_to_normalized_atomic_concepts_left(NormalizedConcept* c) {
	int judy_return;

	J1S(judy_return, normalized_atomic_concepts_left, (Word_t) c);
	if (judy_return == JERR) {
		fprintf(stderr, "could add to normalized atomic concepts on the left handside, aborting!\n");
		exit(-1);
	}
}

/*
void add_to_normalized_atomic_concepts_left(NormalizedConcept* c) {
	NormalizedConcept** tmp;

	tmp = realloc(normalized_atomic_concepts_left, (normalized_atomic_concepts_left_count + 1) * sizeof(NormalizedConcept*));
	assert(tmp != NULL);
	normalized_atomic_concepts_left = tmp;
	normalized_atomic_concepts_left[normalized_atomic_concepts_left_count] = c;
	++normalized_atomic_concepts_left_count;
}
*/


/*
void add_to_normalized_atomic_concepts(NormalizedConcept* c, enum axiom_side side) {
	PWord_t pvalue;

	switch (side) {
		case LEFT_HANDSIDE:
			JSLI(pvalue, normalized_atomic_concepts_left, (unsigned char*) c->description.atomic->name);
			if (pvalue == PJERR) {
				fprintf(stderr, "could not insert normalized atomic concept, aborting\n");
				exit(-1);
			}
			*pvalue = (Word_t) c;
			break;
		case RIGHT_HANDSIDE:
			JSLI(pvalue, normalized_atomic_concepts_right, (unsigned char*) c->description.atomic->name);
			if (pvalue == PJERR) {
				fprintf(stderr, "could not insert normalized atomic concept, aborting\n");
				exit(-1);
			}
			*pvalue = (Word_t) c;
			break;
	}
}
*/

/*
NormalizedConcept* get_normalized_atomic_concept(char* name, enum axiom_side side) {
	PWord_t pvalue;
	switch (side) {
		case LEFT_HANDSIDE:
			JSLG(pvalue, normalized_atomic_concepts_left, (unsigned char*) name);
			break;
		case RIGHT_HANDSIDE:
			JSLG(pvalue, normalized_atomic_concepts_right, (unsigned char*) name);
			break;
	}
	if (pvalue == NULL)
		return NULL;
	return (NormalizedConcept*) *pvalue;
}
*/
int compare_concepts(const void* c1, const void* c2) {
	if (c1 < c2) {
		// printf("%p < %p\n", c1, c2);
		return -1;
	}
	if (c1 > c2) {
		// printf("%p > %p\n", c1, c2);
		return 1;
	}
	// printf("%p = %p\n", c1, c2);
	return 0;
}

NormalizedConcept* get_normalized_conjunction(NormalizedConcept** conjuncts, int size) {
	int i;
	char buffer[128];
	char tmp[11];
	PWord_t pvalue;

	Pvoid_t x = (Pvoid_t) NULL;
	Word_t index = 0;
	int judy_return;

	buffer[0] = '\0';
	tmp[0] = '\0';

	for (i = 0; i < size; ++i) {
		J1S(judy_return, x, (Word_t) conjuncts[i]);
		if (judy_return == JERR) {
			fprintf(stderr, "could not set x, aborting!\n");
			exit(-1);
		}
	}
/*
	qsort(conjuncts, size, sizeof(NormalizedConcept*), compare_concepts);
	for (i = 0; i < size; ++i) {
		printf("XXX:%p:\n", conjuncts[i]);
		snprintf(tmp, 10, "%p", conjuncts[i]);
		strcat(buffer, tmp);
		strcat(buffer, "_");
	}
*/
	J1F(judy_return, x, index);
	while (judy_return == 1) {
		snprintf(tmp, 10, "%p", (NormalizedConcept*) index);
		strcat(buffer, tmp);
		strcat(buffer, "_");
		J1N(judy_return, x, index);	
	}
	// printf("get normalized_conjunction:%s:\n", buffer);

	JSLG(pvalue, normalized_conjunctions, (unsigned char*) buffer); 
	if (pvalue == NULL) {
		// printf("not found\n");
		return NULL;
	}
	// printf("FOUND!\n");
	return (NormalizedConcept*) *pvalue;
}

void put_normalized_conjunction(NormalizedConcept* conjunction) {
	NormalizedConcept** conjuncts;
	int conjunct_count, judy_return, i;
	Word_t index;
	PWord_t pvalue;
	char buffer[128];
	char tmp[11];

	buffer[0] = '\0';
	tmp[0] = '\0';
	J1C(conjunct_count, conjunction->description.conj->conjuncts, 0, -1);
	conjuncts = (NormalizedConcept**) malloc(conjunct_count * sizeof(NormalizedConcept*));
	assert(conjuncts != NULL);
	i = 0;
	index = 0;
	J1F(judy_return, conjunction->description.conj->conjuncts, index);
	while (judy_return == 1) {
		conjuncts[i++] = (NormalizedConcept*) index;
		J1N(judy_return, conjunction->description.conj->conjuncts, index);
	}
	// qsort(conjuncts, conjunct_count, sizeof(NormalizedConcept*), compare_concepts);
	for (i = 0; i < conjunct_count; ++i) {
		// printf("yyy:%p:\n", conjuncts[i]);
		snprintf(tmp, 10, "%p", conjuncts[i]);
		strcat(buffer, tmp);
		strcat(buffer, "_");
	}
	free(conjuncts);
	// printf("put normalized_conjunction:%s:\n", buffer);
	JSLI(pvalue, normalized_conjunctions, (unsigned char*) buffer);
	if (pvalue == PJERR) {
		fprintf(stderr, "could not insert normalized conjunction, aborting\n");
		exit(-1);
	}
	*pvalue = (Word_t) conjunction;
/*
	PWord_t pvalue;
	int i, has_next_conjunct;
	Word_t conjunct_index = 0;

	J1F(has_next_conjunct, conjunction->description.conj->conjuncts, conjunct_index); 	
	while (has_next_conjunct == 0) {
		JLG(pvalue, normalized_conjunctions, conjunct_index);
		if (pvalue == NULL) {
			JLI(pvalue, normalized_conjunctions, conjunct_index);
			if (pvalue == PJERR) {
				fprintf(stderr, "could not insert normalized conjunction, aborting!\n");
				exit(-1);
			}
			*pvalue = 
		}
	}
*/

	
}
